#include "stdio.h"

#include "global.h"
#include "stdint.h"
#include "string.h"
#include "syscall-handler.h"

#define va_start(ap, v) ap = (va_list) & v

#define va_arg(ap, t) *((t*)(ap += 4))

#define va_end(ap) ap = NULL

/**
 * @brief formatted output using a va_list argument pointer to the specified
 * string buffer.
 *
 * @param str Pointer to the destination string where the formatted data will be
 * written.
 * @param format Pointer to the format string that specifies how subsequent
 * arguments are converted for output.
 * @param ap Pointer to a va_list containing the variable arguments to be
 * formatted and written to the string.
 *
 * @return The number of characters written to the string, excluding the
 * null-terminating character.
 */
uint32_t vsprintf(char* str, const char* format, va_list ap)
{
  // Pointer to the current position in the buffer string
  char* buf_ptr = str;
  // Pointer to the current position in the format string
  const char* index_ptr = format;

  while (*index_ptr)
  {
    if (*index_ptr != '%')
    {
      // copy non-placeholder character to new string
      *(buf_ptr++) = *(index_ptr++);
    }
    else
    {
      index_ptr++; // Move past the '%'

      uint32_t arg_int;
      char*    arg_str;
      // deal with placeholder
      switch (*index_ptr)
      {
      // Integer placeholder
      case 'd':
        arg_int = va_arg(ap, uint32_t);
        itoa(arg_int, &buf_ptr, 10);
        index_ptr++;
        break;

        // Hexadecimal placeholder
      case 'x':
        arg_int = va_arg(ap, uint32_t);
        itoa(arg_int, &buf_ptr, 16);
        index_ptr++;
        break;

        // Character placeholder
      case 'c':
        *(buf_ptr++) = va_arg(ap, char);
        index_ptr++;
        break;

        // String placeholder
      case 's':
        arg_str = va_arg(ap, char*);
        strcpy(arg_str, buf_ptr);
        buf_ptr += strlen(arg_str);
        index_ptr++;
        break;
      }
    }
  }
  return strlen(str);
}

uint32_t printf(const char* restrict format, ...)
{
  va_list args;
  va_start(args, format);
  char buf[1024] = {0};
  vsprintf(buf, format, args);
  va_end(args);
  return write(buf);
}

uint32_t sprintf(char* buf, const char* restrict format, ...)
{
  va_list args;
  va_start(args, format);
  uint32_t retval = vsprintf(buf, format, args);
  va_end(args);
  return retval;
}

void itoa(uint32_t value, char** buf_ptr, uint8_t radix)
{
  // Calculate the least significant digit
  uint32_t remainder = value % radix;

  // Calculate the quotient for further processing
  uint32_t quotient = value / radix;

  // Recursively convert quotient to string if non-zero
  if (quotient)
  {
    itoa(quotient, buf_ptr, radix);
  }

  // Convert remainder to character and append to buffer
  if (remainder < 10)
  {
    *((*buf_ptr)++) = remainder + '0'; // Convert to ASCII character
  }
  else
  {
    *((*buf_ptr)++) = remainder - 10 + 'A'; // Convert to ASCII character ('A' - 'F')
  }
}